package cur.nature.tinyorm.core.proxy;

import cur.nature.tinyorm.core.annotation.Column;
import cur.nature.tinyorm.core.annotation.Table;
import cur.nature.tinyorm.core.common.MBean;
import cur.nature.tinyorm.core.common.OrmUtil;

import java.lang.reflect.Field;
import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.time.ZonedDateTime;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.stream.Collectors;

public abstract class AbstractTemplateProxy {
    protected final ConcurrentMap<Class<?>, MBean.M4<Class<?>, String, List<String>, Map<String, String>>> classReflectCache = new ConcurrentHashMap<>();
    protected final ConcurrentMap<Class<?>, Map<String, Field>> fieldReflectCache = new ConcurrentHashMap<>();
    private String doute = "";

    public void setDoute(String doute) {
        this.doute = doute;
    }
    public String getDoute() {
        return doute;
    }


    // 组装参数
    public MBean.M2<String, List<Object>> parseParam(String sql, Map<String, Object> param) {
        List<Object> paramList = new ArrayList<>();
        // 组装参数
        if (param != null && param.size() > 0) {
            for (Map.Entry<String, Object> entry : param.entrySet()) {
                // 循环替换参数
                while (true) {
                    if (!sql.contains("#{" + entry.getKey() + "}")) {
                        break;
                    } // 找不到则退出
                    sql = sql.replaceAll("#\\{" + entry.getKey() + "}", " ? ");
                    paramList.add(entry.getValue());
                }
            }
        }
        return new MBean.M2<>(sql, paramList);
    }

    // 获取所有字段
    public Map<String, Field> getField(Class<?> clazz) {
        // 查询字段
        if (fieldReflectCache.containsKey(clazz)) {
            return fieldReflectCache.get(clazz);
        }
        // 获取字段
        List<Field> list = new ArrayList<>();
        Field[] fields = clazz.getDeclaredFields();
        list.addAll(Arrays.asList(fields));
        // 获取父类字段
        Class<?> superClazz = clazz.getSuperclass();
        if (superClazz != null) {
            Field[] superFields = superClazz.getDeclaredFields();
            list.addAll(Arrays.asList(superFields));
        }
        // 存入map
        Map<String, Field> result = new HashMap<>();
        for (Field field : list) {
            field.setAccessible(true);
            result.put(field.getName().toLowerCase(), field);
        }
        // 加入缓存
        fieldReflectCache.put(clazz, result);
        return result;
    }

    // 从类中获取字段信息
    public MBean.M4<Class<?>, String, List<String>, Map<String, String>> getClassReflet(Class<?> clazz) {
        // 查询缓存
        if (classReflectCache.containsKey(clazz)) {
            return classReflectCache.get(clazz);
        }
        // 获取主表名
        final Table table = clazz.getAnnotation(Table.class);
        final String talbeName = (table != null ? getDoute()+table.name()+getDoute() : null);
        // 获取字段
        final Map<String, String> fieldColMap = new HashMap<>();
        final Map<String, String> colFieldMap = new HashMap<>();
        List<String> idColumn = new ArrayList<>();
        for (Field field : getField(clazz).values()) {
            Column column = field.getDeclaredAnnotation(Column.class);
            final String fieldName = field.getName();
            final String columnName = column.name().toLowerCase();
            if (column.isPK()) { idColumn.add(column.name()); }
            fieldColMap.put(fieldName, getDoute()+columnName+getDoute());
            colFieldMap.put(columnName, fieldName);
        }
        // v1:类, v2:表名, v3:id列名, v4:field->col
        final MBean.M4<Class<?>, String, List<String>, Map<String, String>> result =
                new MBean.M4<>(clazz, talbeName, idColumn, fieldColMap);
        // 加入缓存
        classReflectCache.put(clazz, result);
        return result;
    }

    private boolean isBaseType(Class targetClass) {
        if (targetClass == Map.class
                || targetClass == String.class || targetClass == Integer.class
                || targetClass == Long.class || targetClass == Float.class
                || targetClass == Double.class || targetClass == BigDecimal.class || targetClass == Date.class) {
            return true;
        }
        return false;
    }


    // 将map 转为 对象
    public <T> List<T> parseList(List<Map<String, Object>> resultList, Class<T> targetClass, Map<String, Field> fieldList) throws Exception {
        if (isBaseType(targetClass)) {
            return (List<T>) resultList;
        }
        // 查询并设值
        List<T> result = new ArrayList<>();
        if (resultList != null && !resultList.isEmpty()) {
            // 判断字段是否为空
            if (fieldList == null || fieldList.isEmpty()) {
                fieldList = getField(targetClass);
            }
            // 对字段名称特殊化处理
            Map<String, String> nameSpec = new HashMap<>();
            Map<String, Object> one = resultList.getFirst();
            for (String key : one.keySet()) {
                nameSpec.put(key.replaceAll("_", "").toLowerCase(), key);
            }
            // 设值
            for (Map<String, Object> map : resultList) {
                T object = parseObject(map, targetClass, fieldList, nameSpec);
                result.add(object);
            }
        }
//        LOG.info(JsonUtil.toJSONString(result));
        return result;
    }

    // 将map 转为 对象
    public <T> T parseObject(Map<String, Object> map, Class<T> targetClass,
                             Map<String, Field> fieldList, Map<String, String> nameSpec) throws Exception {
        if (map == null) {
            return null;
        }
        if (targetClass == Map.class) {
            return (T) map;
        }
        // 查询并设值
        Object resObj = targetClass.getDeclaredConstructor().newInstance();
        // 对字段名称特殊化处理
        if (nameSpec == null) {
            nameSpec = new HashMap<>();
            for (String key : map.keySet()) {
                nameSpec.put(key.replaceAll("_", "").toLowerCase(), key);
            }
        }
        // 判断字段是否为空
        if (fieldList == null || fieldList.isEmpty()) {
            fieldList = getField(targetClass);
        }
        // 设值字段
        for (Map.Entry<String, Field> entry : fieldList.entrySet()) {
            Object obj = map.get(nameSpec.get(entry.getKey()));
            setField(resObj, entry.getValue(), obj);
        }
        return (T) resObj;
    }

    // 返回字段名及字段值
    public MBean.M3<Map<String, Object>, String, List<MBean.M2<String, Object>>>
    packUpdateFields(Object obj, boolean isAllColumns) throws IllegalAccessException {
        Map<String, Object> resultMap = new HashMap<>();
        // 查出所有字段
        Collection<Field> fieldList = getField(obj.getClass()).values();
        // 获取主表名
        Table table = obj.getClass().getAnnotation(Table.class);
        String talbeName = (table != null ? getDoute()+table.name()+getDoute() : null);
        List<MBean.M2<String, Object>> idColumn = new ArrayList<>();
        // 拼接字段及字段值
        for (Field field : fieldList) {
            Column column = field.getDeclaredAnnotation(Column.class);
            if (column == null) {
                continue;
            }
            // 主键
            if (column.isPK()) {
                MBean.M2<String, Object> pk = new MBean.M2<>(getDoute()+column.name()+getDoute(), field.get(obj));
                idColumn.add(pk);
                continue;
            }
            // 判断是否为空
            Object value = field.get(obj);
            if (value == null && !isAllColumns) {
                continue;
            }
            //设置值
            resultMap.put(getDoute()+column.name()+getDoute(), value);
        }
        // 返回三个值 v1:字段及字段值, v2:表名, v3: 主键及值
        return new MBean.M3<>(resultMap, talbeName, idColumn);
    }

    // 获取所有字段
    public String getColumns(Class<?> clazz) {
        return getColumns(clazz, null);
    }

    // 获取所有字段
    public String getColumns(Class<?> clazz, final String prefix) {
        // 查询缓存 v1:类, v2:表名, v3:id列名, v4:field->col
        MBean.M4<Class<?>, String, List<String>, Map<String, String>> classCache = getClassReflet(clazz);
        Collection<String> columnList = classCache.v4.values();
        // 组装
        List<String> list = new ArrayList<>(columnList);
        list.replaceAll(s -> getDoute() + s + getDoute());
        if (OrmUtil.isNotEmpty(prefix)) {
            return columnList.stream().map(column -> prefix + column).collect(Collectors.joining(","));
        }
        return String.join(",", columnList);
    }

    // 获取表名
    public String getTableName(Class<?> clazz) {
        // 获取主表名
        Table table = clazz.getAnnotation(Table.class);
        return (table != null ? table.name() : null);
    }

    private void setField(Object origObj, Field field, Object obj) throws Exception {
        if (null == obj) {
            return;
        }
        try {
            field.set(origObj, obj);
        } catch (Exception e) {
            // 设置值失败时, 对值类型进行转换
            Object tmpObj = typeConvert((Class<?>) field.getGenericType(), obj);
            field.set(origObj, tmpObj);
        }
    }

    private <T> T typeConvert(Class<T> targetClass, Object value) throws Exception {
        if (null == value) {
            return null;
        }
        //转换
        if (short.class == targetClass || Short.class.isAssignableFrom(targetClass)) {
            return (T) Short.valueOf(value.toString());
        } else if (int.class == targetClass || Integer.class.isAssignableFrom(targetClass)) {
            return (T) Integer.valueOf(value.toString());
        } else if (long.class == targetClass || Long.class.isAssignableFrom(targetClass)) {
            return (T) Long.valueOf(value.toString());
        } else if (float.class == targetClass || Float.class.isAssignableFrom(targetClass)) {
            return (T) Float.valueOf(value.toString());
        } else if (double.class == targetClass || Double.class.isAssignableFrom(targetClass)) {
            return (T) Double.valueOf(value.toString());
        } else if (BigDecimal.class.isAssignableFrom(targetClass)) {
            return (T) new BigDecimal(value.toString());
        } else if (value != null && value.getClass().isArray() && byte.class == value.getClass().getComponentType()) {
            return (T) new String((byte[]) value, "utf-8");
        } else if (value instanceof LocalDateTime && value.getClass() != targetClass && Date.class.isAssignableFrom(targetClass)) {
            //转换日期
            LocalDateTime ldt = (LocalDateTime) value;
//            ZonedDateTime zdt = ldt.atZone(ZoneId.of("UTC+8"));
            ZonedDateTime zdt = ldt.atZone(ZoneId.systemDefault());
            Date date = Date.from(zdt.toInstant());
            return (T) date;
        } else {
            return (T) value;
        }
    }


}
